/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::collections::BTreeMap;
use chrono::NaiveDateTime;
use openssl::x509::{X509, X509Crl};
use openssl::asn1::Asn1TimeRef;
use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use uuid::Uuid;
use key_management::api::{impls::DefaultCryptoImpl, CryptoOperations};
use serde::Serialize;
use common_log::debug;
use crate::entities::{cert_info, cert_revoked_list};

/// Parses certificate content from PEM format bytes.
///
/// This function takes raw certificate data in PEM format and converts it
/// into an OpenSSL X509 certificate structure for further processing.
///
/// # Arguments
///
/// * `content` - A byte slice containing the PEM-encoded certificate data
///
/// # Returns
///
/// * `Ok(X509)` - Successfully parsed X509 certificate
/// * `Err(ErrorStack)` - OpenSSL error if parsing fails
///
/// # Examples
///
/// ```
/// let pem_data = b"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----";
/// let cert = parse_cert_content(pem_data)?;
/// ```
pub fn parse_cert_content(content: &[u8]) -> Result<X509, ErrorStack> {
    X509::from_pem(content)
}

/// Parses certificate revocation list (CRL) content from PEM format string.
///
/// This function converts a PEM-encoded CRL string into an OpenSSL X509Crl
/// structure that can be used to check certificate revocation status.
///
/// # Arguments
///
/// * `crl_content` - A string slice containing the PEM-encoded CRL data
///
/// # Returns
///
/// * `Ok(X509Crl)` - Successfully parsed X509 CRL
/// * `Err(ErrorStack)` - OpenSSL error if parsing fails
///
/// # Examples
///
/// ```
/// let crl_pem = "-----BEGIN X509 CRL-----\n...\n-----END X509 CRL-----";
/// let crl = parse_crl_content(crl_pem)?;
/// ```
pub fn parse_crl_content(crl_content: &str) -> Result<X509Crl, ErrorStack> {
    X509Crl::from_pem(crl_content.as_bytes())
}

/// Converts an OpenSSL `Asn1TimeRef` to a Unix timestamp (seconds).
///
/// This function takes an ASN.1 time reference from an X.509 certificate
/// and converts it to a Unix timestamp for easier date/time manipulation.
/// The conversion handles the standard ASN.1 time format used in certificates.
///
/// # Arguments
///
/// * `asn1_time` - A reference to the `Asn1TimeRef` to convert
///
/// # Returns
///
/// * `Ok(i64)` - Unix timestamp in seconds since epoch
/// * `Err(Box<dyn std::error::Error>)` - Error if time parsing fails
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_data)?;
/// let not_after = cert.not_after();
/// let timestamp = asn1_time_to_timestamp(not_after)?;
/// ```
pub fn asn1_time_to_timestamp(asn1_time: &Asn1TimeRef) -> Result<i64, Box<dyn std::error::Error>> {
    // Convert Asn1Time to a string
    let time_str = asn1_time.to_string();

    // Parse the time string as chrono:: NaiveDATE
    let naive_time = NaiveDateTime::parse_from_str(&time_str, "%b %e %H:%M:%S %Y GMT")?;

    // Convert to Unix timestamp
    Ok(naive_time.and_utc().timestamp())
}

/// Generates a unique certificate ID based on serial number, issuer, and user ID.
///
/// This function creates a deterministic UUID v5 based certificate identifier
/// by combining the certificate's serial number, issuer information, and user ID.
/// The resulting ID is consistent for the same input parameters and can be used
/// for certificate tracking and management.
///
/// # Arguments
///
/// * `serial_num` - The certificate's serial number as a string
/// * `issuer` - The certificate issuer's distinguished name
/// * `user_id` - The user identifier associated with the certificate
///
/// # Returns
///
/// A string containing the generated certificate ID (UUID v5 without hyphens)
///
/// # Examples
///
/// ```
/// let cert_id = generate_cert_id("123456", "CN=Example CA", "user123");
/// assert_eq!(cert_id.len(), 32); // UUID without hyphens
/// ```
pub fn generate_cert_id(serial_num: &str, issuer: &str, user_id: &str) -> String {
    // Combine fields into a string
    let combined = format!("{}-{}-{}", serial_num, issuer, user_id);

    // Generate UUID using UUID v5
    let namespace = Uuid::NAMESPACE_OID; // Using OID namespace
    Uuid::new_v5(&namespace, combined.as_bytes()).to_string().replace("-", "")
}

/// Extracts and formats the serial number from an X.509 certificate.
///
/// This function retrieves the certificate's serial number, converts it to
/// a big number representation, and then encodes it as a hexadecimal string.
/// Returns an empty string if the serial number cannot be extracted.
///
/// # Arguments
///
/// * `cert` - A reference to the X509 certificate
///
/// # Returns
///
/// A string containing the hexadecimal representation of the certificate's serial number,
/// or an empty string if extraction fails
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_data)?;
/// let serial = get_cert_serial_number(&cert);
/// println!("Certificate serial: {}", serial);
/// ```
pub fn get_cert_serial_number(cert: &X509) -> String {
    let serial_bn = cert.serial_number().to_bn().map_err(|_| return "".to_string()).unwrap();
    hex::encode(serial_bn.to_vec())
}

/// Extracts and formats the issuer name from an X.509 certificate.
///
/// This function retrieves all issuer name entries from the certificate,
/// converts them to a structured format (BTreeMap), and serializes the
/// result as a JSON string. This provides a consistent representation
/// of the certificate issuer's distinguished name.
///
/// # Arguments
///
/// * `cert` - A reference to the X509 certificate
///
/// # Returns
///
/// A JSON string representation of the issuer name entries,
/// or an empty string if serialization fails
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_data)?;
/// let issuer = get_cert_issuer_name(&cert);
/// println!("Issuer: {}", issuer);
/// ```
pub fn get_cert_issuer_name(cert: &X509) -> String {
    let map = cert
        .issuer_name()
        .entries()
        .into_iter()
        .map(|e| (e.object().to_string(), e.data().as_utf8().map(|x| x.to_string()).unwrap_or("".to_string())))
        .collect::<BTreeMap<String, String>>();
    serde_json::to_string(&map).unwrap_or("".to_string())
}

/// Extracts and formats the subject name from an X.509 certificate.
///
/// This function retrieves all subject name entries from the certificate,
/// converts them to a structured format (BTreeMap), and serializes the
/// result as a JSON string. This provides a consistent representation
/// of the certificate subject's distinguished name.
///
/// # Arguments
///
/// * `cert` - A reference to the X509 certificate
///
/// # Returns
///
/// A JSON string representation of the subject name entries,
/// or an empty string if serialization fails
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_data)?;
/// let subject = get_cert_subject_name(&cert);
/// println!("Subject: {}", subject);
/// ```
pub fn get_cert_subject_name(cert: &X509) -> String {
    let map = cert
        .subject_name()
        .entries()
        .into_iter()
        .map(|e| (e.object().to_string(), e.data().as_utf8().map(|x| x.to_string()).unwrap_or("".to_string())))
        .collect::<BTreeMap<String, String>>();
    serde_json::to_string(&map).unwrap_or("".to_string())
}

/// Extracts and formats the issuer name from an X.509 Certificate Revocation List (CRL).
///
/// This function retrieves all issuer name entries from the CRL,
/// converts them to a structured format (BTreeMap), and serializes the
/// result as a JSON string. This provides a consistent representation
/// of the CRL issuer's distinguished name.
///
/// # Arguments
///
/// * `crl` - A reference to the X509Crl (Certificate Revocation List)
///
/// # Returns
///
/// A JSON string representation of the CRL issuer name entries,
/// or an empty string if serialization fails
///
/// # Examples
///
/// ```
/// let crl = X509Crl::from_pem(crl_data)?;
/// let issuer = get_crl_issuer_name(&crl);
/// println!("CRL Issuer: {}", issuer);
/// ```
pub fn get_crl_issuer_name(crl: &X509Crl) -> String {
    let map = crl
        .issuer_name()
        .entries()
        .into_iter()
        .map(|e| (e.object().to_string(), e.data().as_utf8().map(|x| x.to_string()).unwrap_or("".to_string())))
        .collect::<BTreeMap<String, String>>();
    serde_json::to_string(&map).unwrap_or("".to_string())
}

/// Verifies if a certificate is still valid based on its expiration time.
///
/// This function checks whether the given X509 certificate has expired
/// by comparing its 'not after' time with the current UTC timestamp.
///
/// # Arguments
///
/// * `cert` - A reference to the X509 certificate to verify
///
/// # Returns
///
/// * `true` - If the certificate is still valid (not expired)
/// * `false` - If the certificate has expired or if there's an error parsing the expiration time
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_pem_data)?;
/// if verify_cert_time(&cert) {
///     println!("Certificate is still valid");
/// } else {
///     println!("Certificate has expired");
/// }
/// ```
pub fn verify_cert_time(cert: &X509) -> bool {
    let timestamp = chrono::Utc::now().timestamp();
    if let Ok(expiration_time) = asn1_time_to_timestamp(cert.not_after()) {
        return expiration_time > timestamp;
    }
    false
}

/// Determines the appropriate message digest algorithm based on the certificate's signature algorithm.
///
/// This function examines the signature algorithm of an X509 certificate and returns
/// the corresponding MessageDigest that should be used for cryptographic operations.
/// It supports common algorithms including ECDSA and RSA with various hash functions,
/// as well as the Chinese SM2-with-SM3 algorithm.
///
/// # Arguments
///
/// * `cert` - A reference to the X509 certificate whose signature algorithm to analyze
///
/// # Returns
///
/// * `Some(MessageDigest)` - The appropriate digest algorithm for the certificate
/// * `None` - If the signature algorithm is not supported or recognized
///
/// # Supported Algorithms
///
/// - ECDSA_WITH_SHA256 / SHA256WITHRSAENCRYPTION → SHA256
/// - ECDSA_WITH_SHA384 / SHA384WITHRSAENCRYPTION → SHA384  
/// - ECDSA_WITH_SHA512 / SHA512WITHRSAENCRYPTION → SHA512
/// - SM2-with-SM3 → SM3
///
/// # Examples
///
/// ```
/// let cert = X509::from_pem(cert_pem_data)?;
/// if let Some(digest) = get_cert_digest(&cert) {
///     println!("Using digest algorithm: {:?}", digest);
/// } else {
///     println!("Unsupported signature algorithm");
/// }
/// ```
pub fn get_cert_digest(cert: &X509) -> Option<MessageDigest> {
    // Obtain signature algorithm
    let sig_alg = cert.signature_algorithm().object();
    // Judging Abstract Algorithm Based on NID
    match sig_alg.nid() {
        Nid::ECDSA_WITH_SHA256 | Nid::SHA256WITHRSAENCRYPTION => Some(MessageDigest::sha256()),
        Nid::ECDSA_WITH_SHA384 | Nid::SHA384WITHRSAENCRYPTION => Some(MessageDigest::sha384()),
        Nid::ECDSA_WITH_SHA512 | Nid::SHA512WITHRSAENCRYPTION => Some(MessageDigest::sha512()),
        _ => {
            if sig_alg.to_string().eq("SM2-with-SM3") {
                Some(MessageDigest::sm3())
            } else {
                None
            }
        },
    }
}

/// Verifies the validity of an X.509 certificate.
///
/// This function checks the certificate's validity period and verifies
/// if its signature algorithm and key size/curve are supported.
///
/// # Arguments
/// * `cert` - A reference to the `X509` certificate to verify.
///
/// # Returns
/// `true` if the certificate is valid according to the checks, `false` otherwise.
pub fn verify_cert(cert: &X509) -> bool {
    if !verify_cert_time(cert) {
        return false;
    }

    let digest = get_cert_digest(cert);
    if digest.is_none() {
        return false;
    }
    // Check if certificate signature algorithm is valid
    cert.public_key()
        .map(|pk| match pk.id() {
            openssl::pkey::Id::RSA => match pk.bits() {
                2048 | 3072 | 4096 => {
                    debug!("RSA size: {:?}", pk.bits());
                    true
                },
                _ => false,
            },
            openssl::pkey::Id::EC => pk
                .ec_key()
                .map(|ec_key| {
                    ec_key
                        .group()
                        .curve_name()
                        .map(|curve_name| match curve_name {
                            Nid::X9_62_PRIME256V1 => true,
                            Nid::SECP521R1 => true,
                            _ => false,
                        })
                        .unwrap_or(false)
                })
                .unwrap_or(false),
            _ => digest.unwrap() == MessageDigest::sm3(),
        })
        .unwrap_or(false)
}

/// Gets a static reference to the crypto operations implementation.
///
/// This function returns a static instance of DefaultCryptoImpl to avoid
/// creating new instances on each call, improving performance.
///
/// # Returns
/// A static reference to DefaultCryptoImpl that implements CryptoOperations.
pub fn get_crypto_operations() -> &'static DefaultCryptoImpl {
    static CRYPTO_OPERATIONS: DefaultCryptoImpl = DefaultCryptoImpl;
    &CRYPTO_OPERATIONS
}

/// Gets the signature for a given certificate model.
///
/// This function prepares the certificate model by clearing existing signature
/// and key information before obtaining a new signature using the configured
/// crypto operations.
///
/// # Arguments
/// * `cert` - A reference to the `cert_info::Model` to be signed.
///
/// # Returns
/// A tuple containing an optional signature as a byte vector and an optional
/// key version string. Returns `(None, None)` if signing is not required
/// or fails.
pub async fn get_cert_signature(cert: &cert_info::Model) -> (Option<Vec<u8>>, Option<String>) {
    let mut cert_sig = cert.clone();
    cert_sig.signature = None;
    cert_sig.key_version = None;
    cert_sig.key_id = None;
    cert_sig.valid_code = None;
    get_signature(&cert_sig).await
}

/// Gets the signature for a given revoked certificate model.
///
/// This function prepares the revoked certificate model by clearing existing
/// signature and key information before obtaining a new signature using the
/// configured crypto operations.
///
/// # Arguments
/// * `cert_revoked_model` - A reference to the `cert_revoked_list::Model` to be signed.
///
/// # Returns
/// A tuple containing an optional signature as a byte vector and an optional
/// key version string. Returns `(None, None)` if signing is not required
/// or fails.
pub async fn get_revoke_cert_signature(
    cert_revoked_model: &cert_revoked_list::Model,
) -> (Option<Vec<u8>>, Option<String>) {
    let mut cert_sig = cert_revoked_model.clone();
    cert_sig.signature = None;
    cert_sig.key_version = None;
    cert_sig.key_id = None;
    cert_sig.valid_code = None;
    get_signature(&cert_sig).await
}

/// Gets a signature for any serializable data structure.
///
/// This function serializes the input data to JSON and signs it using
/// the crypto operations if signing is required.
///
/// # Arguments
/// * `info` - A reference to any type that implements Serialize.
///
/// # Returns
/// A tuple containing an optional signature as a byte vector and an optional
/// key version string. Returns `(None, None)` if signing is not required
/// or fails.
pub async fn get_signature(info: &impl Serialize) -> (Option<Vec<u8>>, Option<String>) {
    let crypto_operations = get_crypto_operations();
    if !crypto_operations.is_require_sign().await.unwrap_or(false) {
        return (None, None);
    }
    let data = serde_json::to_string(info).unwrap_or("".to_string());
    debug!("get_signature:{}", data);
    match crypto_operations.sign(&data.into_bytes(), "FSK").await {
        Ok(res) => (Some(res.signature), Some(res.key_version)),
        Err(_) => (None, None),
    }
}

/// Verifies a signature for any serializable data structure.
///
/// This function serializes the input data to JSON and verifies its signature
/// using the crypto operations if signing is required.
///
/// # Arguments
/// * `info` - A reference to any type that implements Serialize.
/// * `signature_opt` - Optional signature bytes to verify.
/// * `key_version_opt` - Optional key version string for verification.
///
/// # Returns
/// `true` if the signature is valid or signing is not required, `false` otherwise.
pub async fn verify_signature(
    info: &impl Serialize,
    signature_opt: Option<Vec<u8>>,
    key_version_opt: Option<String>,
) -> bool {
    let crypto_operations = get_crypto_operations();
    if !crypto_operations.is_require_sign().await.unwrap_or(false) {
        return true
    }
    if let (Some(signature), Some(key_version)) = (signature_opt, key_version_opt) {
        let data = serde_json::to_string(info).unwrap_or("".to_string());
        debug!("verify_signature:{}", data);
        return crypto_operations
            .verify("FSK", Some(key_version.as_str()), data.into_bytes(), signature)
            .await
            .unwrap_or_else(|_| false)
    }
    false
}